/* 
 * Bitmain Tech, Home of Professional Bitmain Tech
 * Copyright 2014 Bitmain Tech Inc. and/or its affiliates and other contributors
 * as indicated by the @author tags. All rights reserved.
 * See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 */
package com.bitmain.mmst.web.service;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;

import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;

import com.bitmain.mmst.miner.monitor.executor.AbstractMinerExecutor;
import com.bitmain.mmst.miner.monitor.executor.MinerCommandMessage;
import com.bitmain.mmst.miner.monitor.internal.MonitorOption;

public class ShelfDetectExecutor extends AbstractMinerExecutor<ShelfDetectExecutor, MinerCommandMessage> {
	
	private MinerCommandMessage message;
	
	public ShelfDetectExecutor(MinerCommandMessage message) {
		super(ShelfDetectExecutor.class);
		this.message = message;
	}
	
	private String request(String host, int port, String cmd) {
		Socket socket = null;
		BufferedWriter writer = null;
		BufferedReader reader = null;
		StringBuilder response = new StringBuilder();
		try {
			socket = new Socket();
			socket.setSoTimeout(MonitorOption.socketOvertime);
			socket.connect(new InetSocketAddress(host, port));
			
			writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
			writer.write(cmd);
			writer.flush();
			
			reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			String line;
			while ((line = reader.readLine()) != null) {
				response.append(line);
			}
			return response.toString().trim();
		} catch (Exception e) {
//			StringBuilder log = new StringBuilder();
//			log.append("\r\n\t!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
//			log.append("\r\n\tFailed socket-command-line-request - Miner : { host=").append(host).append(", port=").append(port).append(", command=").append(cmd).append(" }");
//			log.append("\r\n\t!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
//			logger.error(log.toString(), e);
			return null;
		} finally {
			try {
				if (reader != null) {
					reader.close();
					reader = null;
				}
				if (writer != null) {
					writer.close();
					writer = null;
				}
				if (socket != null) {
					socket.close();
					socket = null;
				}
			} catch (Exception e) {
//				StringBuilder log = new StringBuilder();
//				log.append("\r\n\t!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
//				log.append("\r\n\tAn exception occurred at the end of socket-command-line-request - Miner : { host=").append(host).append(", port=").append(port).append(", command=").append(cmd).append(" }");
//				log.append("\r\n\t!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
//				logger.warn(log.toString(), e);
			}
		}
	}
	
	/* (non-Javadoc)
	 * @see com.bitmain.mmst.core.monitor.executor.AbstractMinerExecutor#execute()
	 */
	@Override
	public MinerCommandMessage execute() {
		try {
			int count = 0;
			do {
				message = doExecute();
				count ++;
			} while (BooleanUtils.isNotTrue(message.getCommandable()) && count < 3);
		} catch (Exception e) {
//			StringBuilder log = new StringBuilder();
//			log.append("\r\n\t!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
//			log.append("\r\n\tAn exception occurred when the executor executes.");
//			log.append("\r\n\t!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
//			logger.warn(log.toString(), e);
		}
		return message;
	}
	
	public MinerCommandMessage doExecute() {
		String host = message.getHost();
		int port = message.getPort();
		String mac = getMinerMac(host);
		if (StringUtils.isNotBlank(mac)) {
			message.setMacable(true);
			message.setMac(mac);
		} else {
			message.setMacable(false);
		}
		if (message.getMacable()) {
			String stats = request(host, port, Command.STATS.value);
			if (StringUtils.isNotBlank(stats)) {
				message.setStatsable(true);
				message.setStats(stats);
			} else {
				message.setStatsable(false);
			}
			String pools = request(host, port, Command.POOLS.value);
			if (StringUtils.isNotBlank(pools)) {
				message.setPoolsable(true);
				message.setPools(pools);
			} else {
				message.setPoolsable(false);
			}
			String summary = request(host, port, Command.SUMMARY.value);
			if (StringUtils.isNotBlank(summary)) {
				message.setSummaryable(true);
				message.setSummary(summary);
			} else {
				message.setSummaryable(false);
			}
		}
		return message;
	}

	/**
	 * 矿机命令行枚举
	 *
	 * @author Kweny - enkun.jiang@bitmain.com
	 * @since 2015年11月24日
	 *
	 */
	public static enum Command {
		/**
		 * 矿机MAC地址命令
		 */
		MAC("", 1 << 1),
		/**
		 * 矿机状态信息命令
		 */
		STATS("{\"command\":\"stats\"}", 1 << 2),
		/**
		 * 矿机汇总信息命令
		 */
		SUMMARY("{\"command\":\"summary\"}", 1 << 3),
		/**
		 * 矿机矿池配置信息命令
		 */
		POOLS("{\"command\":\"pools\"}", 1 << 4),
		/**
		 * 所有命令
		 */
		ALL("", 1 << 5);
		
		public int key;
		public String value;
		
		private Command(String value, int key) {
			this.value = value;
			this.key = key;
		}
	}

}
